#ifndef _MESH_MESH_H_
#define _MESH_MESH_H_

#include <string>
#include <vector>
#include <map>
#include <array>
#include <limits>


namespace mesh
{

class mesh;

struct point3f
{ float x,y,z; };

class mesh
{
public:

  typedef point3f vertex;

  mesh(std::string const& filename);

  class triangle
  {
  public:

    std::array<unsigned,3> const&
    get_indices(void) const
    { return _vertices_indices; }

  private:

    friend class mesh;

    std::array<unsigned,3> _vertices_indices;
  };

  class edge
  {
  public:

    std::array<unsigned,2> const&
    get_indices(void) const
    { return _vertices_indices; }

    bool operator <(edge const& e) const
    {
      return (_vertices_indices[0] == e._vertices_indices[0])
          ? (_vertices_indices[1] < e._vertices_indices[1])
          : (_vertices_indices[0] < e._vertices_indices[0]);
    }

  private:

    edge(unsigned v1, unsigned v2)
      : _vertices_indices(
          ((v1 < v2))
          ? std::array<unsigned,2>{v1,v2}
          : std::array<unsigned,2>{v2,v1}) {}

    friend class mesh;

    std::array<unsigned,2> _vertices_indices;
  };

  std::vector<triangle> const& faces(void) const
  { return _faces; }

  std::vector<triangle>& faces(void)
  { return _faces; }

  std::vector<point3f> const& vertices(void) const
  { return _vertices; }

  std::vector<point3f>& vertices(void)
  { return _vertices; }

  std::vector<edge> const& edges(void) const
  { return _edges; }

  vertex const& a(triangle const& t) const
  { return _vertices[t._vertices_indices[0]]; }

  vertex const& b(triangle const& t) const
  { return _vertices[t._vertices_indices[1]]; }

  vertex const& c(triangle const& t) const
  { return _vertices[t._vertices_indices[2]]; }

  vertex& a(triangle const& t)
  { return _vertices[t._vertices_indices[0]]; }

  vertex& b(triangle const& t)
  { return _vertices[t._vertices_indices[1]]; }

  vertex& c(triangle const& t)
  { return _vertices[t._vertices_indices[2]]; }

  unsigned edge_at(unsigned e1, unsigned e2)
  {
    auto it = _vertices_to_edge.find(edge(e1,e2));
    return (it != _vertices_to_edge.end())
        ? it->second
        : std::numeric_limits<unsigned>::max();
  }

protected:

  std::map<edge, unsigned> _vertices_to_edge;
  std::vector<vertex> _vertices;
  std::vector<edge> _edges;
  std::vector<triangle> _faces;

};

} // mesh

#endif // _MESH_MESH_H_
